/*******************************************************************************
 * FilePath    : \Software\Examples\MS1656_app_UART\Src\BSP\BSP.c
 * Version     : 1.0
 * Brief       : 
 * Author      : AndrewHu
 * Company     : Shanghai MacroGiga Electronics CO.,Ltd
 * Email       : Hubinbo@macrogiga.com
 * Date        : 2020-04-03 15:06:14
 * LastEditors : AndrewHu
 * LastEditTime: 2020-09-15 14:20:36
 * Description :  
 ******************************************************************************/
/* Includes ------------------------------------------------------------------*/
#include <Includes.h>
/* Private typedef -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*/
/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
//运行模式，00-normal，01-sleep;02-stop(ble),03-standby
static u8 RunningMode = RunningMode_Normal;
static u8 RunningStatus = 0;      //当前运行状态
static u8 RadioStandbyStatus = 0; //蓝牙射频进入低功耗状态标记
static u8 InterfaceStatus = 0;    //通讯接口
static u8 EnterSleepDelay = 0x00; //进入低功耗延时
static u8 SleepStatus = 0x00;
/* Private function ----------------------------------------------------------*/
/* extern --------------------------------------------------------------------*/
/*******************************************************************************
 * Function    : GetInterfaceStatus
 * Brief       : 获取通讯方式
 * Parameter   : 
 * Returns     : 
 * Date        : 2020-04-17 12:09:56
 * Description : 
 ******************************************************************************/
u8 GetInterfaceStatus(void)
{
    return InterfaceStatus;
}
/*******************************************************************************
 * Function    : ClearEnterSleepDelay
 * Brief       : 延时进入休眠
 * Parameter   : 
 * Returns     : 
 * Date        : 2020-04-17 13:28:22
 * Description : 
 ******************************************************************************/
void ClearEnterSleepDelay(void)
{
    EnterSleepDelay = 0x00;
}
/*******************************************************************************
 * Function    : GetRunningMode
 * Brief       : 获取当前运行模式
 * Parameter   : 
 * Returns     : 
 * Date        : 2020-04-06 00:41:31
 * Description : 
 ******************************************************************************/
u8 GetRunningMode(void)
{
    return RunningMode;
}
/*******************************************************************************
 * Function    : GetRunningStatus
 * Brief       : 获取当前运行状态
 * Parameter   : 
 * Returns     : 
 * Date        : 2020-04-17 13:37:49
 * Description : 
 ******************************************************************************/
u8 GetRunningStatus(void)
{
    return RunningStatus;
}
/*******************************************************************************
 * Function    : SetRunningMode
 * Brief       : 设定运行模式
 * Parameter   : 
 * Returns     : 
 * Date        : 2020-04-17 13:37:51
 * Description : 
 ******************************************************************************/
void SetRunningMode(u8 mode)
{
    RunningMode = mode;
}
/*******************************************************************************
 * Function    : SetRunningStatus
 * Brief       : 
 * Parameter   : 
 * Returns     : 
 * Date        : 2020-04-23 15:53:09
 * Description : 
 ******************************************************************************/
void SetRunningStatus(u8 status)
{
    RunningStatus = status;
}
/*******************************************************************************
 * Function    : GPIO_Init
 * Brief       : 主要用于判断通讯接口选择
 * Parameter   : 
 * Returns     : 
 * Date        : 2020-04-16 18:38:14
 * Description : PA2/PA3组合判断通讯方式
 *               00-Interface_Uart0
 *               01-Interface_Uart1
 *               10-Interface_I2C
 ******************************************************************************/
static void GPIO_Init(void)
{
    GPIO_InitTypeDef GPIO_InitStruct;

    __HAL_RCC_GPIOA_CLK_ENABLE();

    GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
    GPIO_InitStruct.OpenDrain = GPIO_PUSHPULL;
    GPIO_InitStruct.Debounce.Enable = GPIO_DEBOUNCE_DISABLE;
    GPIO_InitStruct.SlewRate = GPIO_SLEW_RATE_HIGH;
    GPIO_InitStruct.DrvStrength = GPIO_DRV_STRENGTH_HIGH;
    GPIO_InitStruct.Pull = GPIO_PULLDOWN; //下拉输入
    GPIO_InitStruct.Alternate = 0x00;     //不复用

    GPIO_InitStruct.Pin = GPIO_PIN_2 | GPIO_PIN_3;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

    Delay_ms(5); //延时5ms

    if (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_2))
    {
        InterfaceStatus |= 0x02u;

        GPIO_InitStruct.Pull = GPIO_PULLDOWN; //上拉输入,防止漏电
        GPIO_InitStruct.Pin = GPIO_PIN_2;
        HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
    }
    else
    {
        InterfaceStatus &= ~0x02u;
    }

    if (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_3))
    {
        InterfaceStatus |= 0x01u;

        GPIO_InitStruct.Pull = GPIO_PULLDOWN; //上拉输入,防止漏电
        GPIO_InitStruct.Pin = GPIO_PIN_3;
        HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
    }
    else
    {
        InterfaceStatus &= ~0x01u;
    }

    //通讯模式确认
    switch (InterfaceStatus)
    {
    case 0x01:
        InterfaceStatus = Interface_Uart1;
        break;
    case 0x02:
        InterfaceStatus = Interface_I2C;
        break;
    default:
        InterfaceStatus = Interface_Uart0;
        break;
    }
}
/*******************************************************************************
 * Function    : 
 * Brief       : 
 * Parameter   : 
 * Returns     : 
 * Date        : 2020-06-23 16:36:17
 * Description : 
 ******************************************************************************/
void SystemClock_Config(void)
{
    RCC_OscInitTypeDef RCC_OscInitStruct = {0};
    RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};

    RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HIRC;
    RCC_OscInitStruct.HIRCState = RCC_HIRC_ON;

    RCC_OscInitStruct.HIRCCalibrationValue = RCC_HIRCCALIBRATION_24M;

    if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
    {
        Error_Handler();
    }

    /**Initializes the CPU, AHB and APB busses clocks 
    */
    RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_PCLK;
    RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_HIRC;
    RCC_ClkInitStruct.AHBCLKDivider = RCC_HCLK_DIV1;
    RCC_ClkInitStruct.APBCLKDivider = RCC_PCLK_DIV1;

    if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct) != HAL_OK)
    {
        Error_Handler();
    }
}
/*******************************************************************************
 * Function    : AH_SystemClock_Config
 * Brief       : 系统时钟初始化函数，初始化之前先复位所有时钟
 * Parameter   : {u8} freq 0-4M,1-24M
 * Returns     : 
 * Date        : 2020-04-03 15:29:16
 * Description : 
 ******************************************************************************/
void AH_SystemClock_Config(u8 freq)
{
    RCC_OscInitTypeDef RCC_OscInitStruct = {0};
    RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
    static u8 SystemClock24M = 0;

    if (SystemClock24M == freq)
        return;
    SystemClock24M = freq;

    RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HIRC;
    RCC_OscInitStruct.HIRCState = RCC_HIRC_ON;

    if (SystemClock24M)
        RCC_OscInitStruct.HIRCCalibrationValue = RCC_HIRCCALIBRATION_24M;
    else
        RCC_OscInitStruct.HIRCCalibrationValue = RCC_HIRCCALIBRATION_4M;

    if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
    {
        Error_Handler();
    }

    /**Initializes the CPU, AHB and APB busses clocks 
    */
    RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_PCLK;
    RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_HIRC;
    RCC_ClkInitStruct.AHBCLKDivider = RCC_HCLK_DIV1;
    RCC_ClkInitStruct.APBCLKDivider = RCC_PCLK_DIV1;

    if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct) != HAL_OK)
    {
        Error_Handler();
    }
}
/*******************************************************************************
 * Function    : IRQ_Init
 * Brief       : 蓝牙中断设置
 * Parameter   : 
 * Returns     : 
 * Date        : 2020-04-03 16:59:42
 * Description : 
 ******************************************************************************/
void IRQ_Init(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;

    __HAL_RCC_GPIOB_CLK_ENABLE();

    //蓝牙中断
    GPIO_InitStructure.Pin = GPIO_PIN_4;
    GPIO_InitStructure.Pull = GPIO_PULLUP;
    GPIO_InitStructure.Debounce.Enable = GPIO_DEBOUNCE_DISABLE;
    GPIO_InitStructure.SlewRate = GPIO_SLEW_RATE_HIGH;
    GPIO_InitStructure.DrvStrength = GPIO_DRV_STRENGTH_HIGH;
    /* Configure Button pin as input */
    GPIO_InitStructure.Mode = GPIO_MODE_INPUT;
    HAL_GPIO_Init(GPIOB, &GPIO_InitStructure);

    /* Configure Button pin as input with External interrupt */
    GPIO_InitStructure.Mode = EXTI_MODE;
    GPIO_InitStructure.Exti.Enable = GPIO_EXTI_INT_ENABLE;
    GPIO_InitStructure.Exti.EdgeLevelSel = GPIO_EXTI_INT_EDGE;
    GPIO_InitStructure.Exti.RiseFallSel = GPIO_EXTI_INT_LOWFALL;
    HAL_GPIO_Init(GPIOB, &GPIO_InitStructure);

    __HAL_GPIO_EXTI_CLEAR_FLAG(GPIOB, GPIO_PIN_4);
    /* Enable and set Button EXTI Interrupt to the lowest priority */
    HAL_NVIC_SetPriority(GPIOB_IRQn, PRIORITY_HIGH);
    HAL_NVIC_EnableIRQ(GPIOB_IRQn);
}
/*******************************************************************************
 * Function    : BSP_Init
 * Brief       : 硬件（IO）初始化
 * Parameter   : 
 * Returns     : 
 * Date        : 2020-04-03 15:56:40
 * Description : 
 ******************************************************************************/
void BSP_Init(void)
{
    HAL_Init();               //滴答时钟1ms计时
    AH_SystemClock_Config(1); //系统时钟设定
    Flash_Read();
    GPIO_Init();              //通过GPIO判断通讯模式
    SPIM_Init();              //蓝牙通讯IO初始化
    IRQ_Init();               //蓝牙Irq中断设置
    Uart_Init(GetBaudRate()); //串口设置
    // I2C_Init();               //I2C通讯设置
    Led_Init(); //指示灯
    // Key_Init(); //按键
    WDG_Init(); //看门狗使能
}
/*******************************************************************************
 * Function    : Mcu_Sleep
 * Brief       : 芯片MCU部分进入低功耗
 * Parameter   : 
 * Returns     : 
 * Date        : 2020-04-17 13:52:12
 * Description : 
 ******************************************************************************/
void Mcu_Sleep(void)
{
    if (SleepStatus == 0x00)
    {
        Uart_Interrupt_Enable(); //串口唤醒使能
        Key_Interrupt_Enable();  //按键进入休眠
        SleepStatus = 0x01;      //完成低功耗设置
    }

    AH_SystemClock_Config(0); //主频切换为4M
    //休眠指令
    SCB->SCR |= 0x4;
    __WFI();
}
/*******************************************************************************
 * Function    : Mcu_WakeUp
 * Brief       : 芯片退出低功耗
 * Parameter   : 
 * Returns     : 
 * Date        : 2020-06-23 15:42:08
 * Description : 
 ******************************************************************************/
void Mcu_WakeUp(u8 status)
{
    AH_SystemClock_Config(1); //主频切换为24M
    if (status == 1)
    {
        //外设唤醒
        if (SleepStatus == 0x00)
            return;               //非低功耗模式，不重复设定
        Key_Interrupt_Disable();  //按键退出低功耗
        Uart_Interrupt_Disable(); //串口退出低功耗
        SleepStatus = 0x00;       //确认退出低功耗
        ClearEnterSleepDelay();   //进入低功耗延时
        RunningMode = RunningMode_Normal;
        Printf_Wakeup();
    }
}
/*******************************************************************************
 * Function    : RadioStandby
 * Brief       : 蓝牙射频端工作状态
 * Parameter   : 
 * Returns     : 
 * Date        : 2020-04-17 13:56:49
 * Description : 
 ******************************************************************************/
static void RadioStandby(u8 status)
{
    if (RadioStandbyStatus)
    {
        //目前处于休眠状态
        if (status == 0)
            radio_resume(); //唤醒蓝牙
    }
    else
    {
        //目前处于工作状态
        if (status)
            radio_standby(); //关闭蓝牙功能
    }
    RadioStandbyStatus = status; //保存工作状态
}
/*******************************************************************************
 * Function    : IrqMcuGotoSleepAndWakeup
 * Brief       : 运行模式检查及判断
 * Parameter   : 
 * Returns     : 
 * Date        : 2020-04-03 15:58:39
 * Description : 
 ******************************************************************************/
void IrqMcuGotoSleepAndWakeup(void) // auto goto sleep AND wakeup, porting api
{
    if (Get_SWDG_Time() >= 6)
    {
        WDG_ReloadCounter();
        //重新激活蓝牙
        HAL_NVIC_DisableIRQ(GPIOB_IRQn);
        Ble_Init();
        __HAL_GPIO_EXTI_CLEAR_FLAG(GPIOB, GPIO_PIN_4);
        HAL_NVIC_EnableIRQ(GPIOB_IRQn);
        ble_run_interrupt_start(GetBleAdvInterval()); //广播间隔设定，320*0.625=200 ms
        //test
        // Uart_Send_String((u8 *)"IND:radio rest!\r\n", 16);
        return;
    }

    if ((RunningStatus != RunningMode_Stop) && (RunningStatus != RunningMode_Standby))
    {
        //蓝牙射频没有关闭，需要判断是否可以进入休眠接口
        if (ble_run_interrupt_McuCanSleep() == 0)
        {
            //蓝牙不能进入休眠，EnterSleepDelay清零
            ClearEnterSleepDelay();
            return;
        }
    }

    //进入低功耗延时，当EnterSleepDelay清零时开始累计次数
    if ((GetUartStatus() != 0) || (GetKeyStatus() != 0))
    {
        //串口有数据待处理或按键按下，不能进入休眠
        ClearEnterSleepDelay();
        return;
    }
    else if (EnterSleepDelay < 0xf0)
    {
        EnterSleepDelay++;
        return;
    }
    //可以进入休眠，休眠模式判断
    switch (RunningMode)
    {
    case RunningMode_Normal:
        /* Normal ------------------------------------------------------------*/
        RadioStandby(0); //射频工作
        RunningStatus = RunningMode_Normal;
        break;
    case RunningMode_Sleep:
        /* Sleep -------------------------------------------------------------*/
        RadioStandby(0); //蓝牙射频工作
        RunningStatus = RunningMode_Sleep;
        Mcu_Sleep(); //MCU部分进入休眠
        break;
    case RunningMode_Stop:
        /* Stop --------------------------------------------------------------*/
        //省电，蓝牙停止，仅MCU运行，(功耗比sleep大)
        if (GetConnectedStatus())
        {
            ble_disconnect(); //连接状态，先断开蓝牙
            return;
        }

        RadioStandby(1); //蓝牙射频不工作
                         //设定看门狗唤醒和串口唤醒功能
        RunningStatus = RunningMode_Stop;
        WDG_ReloadCounter();
        break;
    case RunningMode_Standby:
        /* Standby -----------------------------------------------------------*/
        //整个系统进入休眠(功耗最低)
        if (GetConnectedStatus())
        {
            ble_disconnect(); //连接状态，先断开蓝牙
            return;
        }

        RadioStandby(1); //蓝牙射频不工作
        LED_OnOff(0);    //关闭指示灯
        RunningStatus = RunningMode_Standby;
        Mcu_Sleep(); //MCU部分进入休眠
        break;
    default:
        /* Else --------------------------------------------------------------*/
        //默认省电模式
        RunningMode = RunningMode_Sleep;
        RunningStatus = RunningMode_Sleep;
        RadioStandby(0); //蓝牙射频工作
        break;
    }
}
/*******************************************************************************
0~3KB BootCode
3~4KB UserData
4~34KB RunCode
34~64KB OTA
*******************************************************************************/
#define FLASH_E2PROM_ADDR_BASE (0x00000000)
//#define FLASH_BOOT_ROM_SIZE    (3*1024)
//#define FLASH_E2PROM_ADDR_USR  (FLASH_E2PROM_ADDR_BASE + FLASH_BOOT_ROM_SIZE)
#define FLASH_E2PROM_ADDR_OTA (FLASH_E2PROM_ADDR_BASE + 34 * 1024)

#define APPLICATION_ADDRESS (uint32_t)0x00001010
/*******************************************************************************
 * Function    : OtaSystemReboot
 * Brief       : 
 * Parameter   : 
 * Returns     : 
 * Date        : 2020-03-23 15:03:36
 * Description : OTA完成后系统重启
 ******************************************************************************/
void OtaSystemReboot(void) //porting api
{
    NVIC_SystemReset();
}
/*******************************************************************************
 * Function    : GetOtaAddr
 * Brief       : 
 * Parameter   : 
 * Returns     : 
 * Date        : 2020-03-23 15:03:54
 * Description : 获取OTA的起始地址
 ******************************************************************************/
u32 GetOtaAddr(void)
{
    return FLASH_E2PROM_ADDR_OTA;
}
/*******************************************************************************
 * Function    : WriteFlashE2PROM
 * Brief       : 
 * Parameter   : 
 * Returns     : 
 * Date        : 2020-03-23 15:03:50
 * Description : OTA时擦写Flash
 ******************************************************************************/
void WriteFlashE2PROM(u8 *data, u16 len, u32 pos, u8 flag) //4 bytes aligned
{
    u32 t;

    while (len >= 4)
    {
        t = data[3];
        t <<= 8;
        t |= data[2];
        t <<= 8;
        t |= data[1];
        t <<= 8;
        t |= data[0];
        HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, pos, t);
        pos += 4;
        len -= 4;
        data += 4;
    }
}
/**
  * @brief  This function is executed in case of error occurrence.
  * @retval None
  */
void Error_Handler(void)
{
    /* USER CODE BEGIN Error_Handler_Debug */
    /* User can add his own implementation to report the HAL error return state */

    /* USER CODE END Error_Handler_Debug */
}

#ifdef USE_FULL_ASSERT
/**
  * @brief  Reports the name of the source file and the source line number
  *         where the assert_param error has occurred.
  * @param  file: pointer to the source file name
  * @param  line: assert_param error line source number
  * @retval None
  */
void assert_failed(uint8_t *file, uint32_t line)
{
    /* USER CODE BEGIN 6 */
    /* User can add his own implementation to report the file name and line number,
     tex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
    /* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */
/*******************************************************************************
 * Function    : 
 * Brief       : 
 * Parameter   : 
 * Returns     : 
 * Date        : 2020-04-03 15:58:17
 * Description : DO NOT REMOVE, used in ble lib
 ******************************************************************************/
void McuGotoSleepAndWakeup(void)
{
}
void SysClk8to48(void)
{
}
void SysClk48to8(void)
{
}
void DisableEnvINT(void)
{
}
void EnableEnvINT(void)
{
}
void UpdateLEDValueAll(void)
{
}
unsigned char aes_encrypt_HW(unsigned char *_data, unsigned char *_key)
{
    return 0;
}
